// -*- c++ -*-
#ifndef __CMATHFIXED_H__
#define __CMATHFIXED_H__


#include "StdDefs.h"
//#include "CClass.h"
#include "SharedSettings.h"



//const ClassId ClassId_fixed = 0x0030ee34;  // CStringToKey( "fixed" );


typedef CMathFixed_Type		fixed;
typedef CMathFixed_Type64	fixed64;


#define CMathFixed_One									( 1 << CMathFixed_NumberOfFractionalBits )
#define CMathFixed_PI									CMathFixed_FloatToFixed( _PI )
#define CMathFixed_PI2									CMathFixed_FloatToFixed( _PI2 )

#define CMathFixed_FloatToFixed(v)						( (fixed)( CMathFixed_One * (v) ) )
#define CMathFixed_FixedToInt32(v)						( (int32)( (v) >> CMathFixed_NumberOfFractionalBits ) )
#define CMathFixed_Int32ToFixed(v)						( (int32)( (v) << CMathFixed_NumberOfFractionalBits ) )

#define CMathFixed_RadiansToDegrees(v)					CMathFixed::Mul( (v), CMathFixed_FloatToFixed( 180.0f / _PI ) )

#if defined( GLfixed_fxp )
	#if( CMathFixed_NumberOfFractionalBits == GLfixed_fxp )
		#define CMathFixed_FixedToGLfixed(v)			(v)
		#define CMathFixed_GLfixedToFixed(v)			(v)
	#else
		#define CMathFixed_FixedToGLfixed(v)			(GLfixed_fxp >= CMathFixed_NumberOfFractionalBits)?			\
															(v)<<(GLfixed_fxp - CMathFixed_NumberOfFractionalBits) :\
															(v)>>(CMathFixed_NumberOfFractionalBits - GLfixed_fxp)
		#define CMathFixed_GLfixedToFixed(v)			(GLfixed_fxp >= CMathFixed_NumberOfFractionalBits)?			\
															(v)>>(GLfixed_fxp - CMathFixed_NumberOfFractionalBits) :\
															(v)<<(CMathFixed_NumberOfFractionalBits - GLfixed_fxp)
	#endif
#endif




class CMathFixed 
{
public:
	static const uint32		FBits;
	static const fixed		One;

	static const fixed		PI;
	static const fixed		PI2;

	static const fixed		Degrees_15;
	static const fixed		Degrees_30;
	static const fixed		Degrees_33;
	static const fixed		Degrees_45;
	static const fixed		Degrees_60;
	static const fixed		Degrees_90;
	static const fixed		Degrees_180;
	static const fixed		Degrees_270;
	static const fixed		Degrees_360;

	static const fixed		UnitTolerance;
	static const fixed		ClampToUnitError;

	//
	static fixed			Abs( fixed val );
	static fixed			Max( fixed val1, fixed val2 );
	static fixed			Min( fixed val1, fixed val2 );

	//
	static fixed			Ceil( fixed val );
	static fixed			Floor( fixed val );
	static fixed			Round( fixed val );

	// 
	static fixed			Div( fixed num, fixed denom );
	static fixed			Mul( fixed val1, fixed val2 );

	//
	static fixed			Inv( fixed val );

	static fixed            Lerp( fixed begin, fixed end, fixed val );
    static fixed            EaseInOut( fixed interpolation );  // input is from 0 to 1, output is from 0 to 1; pass output to Lerp to interpolate between 2 points
    static fixed            EaseIn( fixed interpolation );
    static fixed            EaseOut( fixed interpolation );

	/*
	*  Integer Square Root function
	*  Contributors include Arne Steinarson for the basic approximation idea, Dann 
	*  Corbit and Mathew Hendry for the first cut at the algorithm, Lawrence Kirby 
	*  for the rearrangement, improvements and range optimization and Paul Hsieh 
	*  for the round-then-adjust idea.
	*
	*  Source: http://www.azillionmonkeys.com/qed/sqroot.html
	*  (originally posted on comp.graphics.algorithms)
	*/
	static fixed			Sqrt( fixed val );

	//
	static fixed			GetFraction( fixed val );
	static fixed			Clamp( fixed val, fixed min = -CMathFixed_One, fixed max = CMathFixed_One );
	static fixed			ClampToUnit( fixed val );

	/* 
	* Solves the quadratic equation x = ( -b +/- ( b*b ) - ( 4*a*c) ) / ( 2*a ).
	* Returns true if the roots are real.
	*/
	static boolean			SolveQuadraticForReals( fixed* root1, fixed* root2, 
													fixed a, fixed b, fixed c );
	
	// trig - conv
	static fixed			DegreesToRadians( fixed degrees );
	static fixed			RadiansToDegrees( fixed radians );

	// trig - funcs
	static fixed			ACos( fixed b );
	static fixed			ASin( fixed a );
	static fixed			ATan( fixed h );
    static fixed            ATan2( fixed y, fixed x ); // if x=y, x & y must be less than (int) 32
    static fixed            ATan2i( int32 y, int32 x ); // much higher range of x & y as inputs than the fixed version
	static fixed			Cos( fixed degrees );
	static fixed			Sin( fixed degrees );
	static fixed			Tan( fixed degrees );
};





inline
fixed CMathFixed::Abs( fixed val )
{
	return val > 0 ? val : -val;
}


inline
fixed CMathFixed::Max( fixed val1, fixed val2 )
{
	return ( val1 > val2 )? val1 : val2;
}


inline
fixed CMathFixed::Min( fixed val1, fixed val2 )
{
	return ( val1 < val2 )? val1 : val2;
}


inline
fixed CMathFixed::Ceil( fixed val )
{
	fixed ret;

    ret = Floor( val );

	if( val & ( (~0) ^ ( (~0) << FBits ) ) ) 
		ret	+= One;

	return ret;
}


inline
fixed CMathFixed::Floor( fixed val )
{
	return val & ( (~0) << FBits );
}


inline
fixed CMathFixed::Round( fixed val )
{
	fixed ret;

    ret = Floor( val );

	if( ( val & ( (~0) ^ ( (~0) << FBits ) ) ) >= ( One >> 1 ) )
		ret += One;

	return ret;
}


inline
fixed CMathFixed::Div( fixed num, fixed denom )
{
	fixed64 v64Num = num;
	fixed64 v64Denom = denom;

	return (fixed)( ( v64Num << FBits ) / v64Denom );
}

inline
fixed CMathFixed::Mul( fixed val1, fixed val2 )
{
	fixed64 v64A = val1;
	fixed64 v64B = val2;

	return (fixed)( ( v64A * v64B ) >> FBits );
}


inline
fixed CMathFixed::GetFraction( fixed val )
{
	return val - ( ( val >> FBits ) << FBits );
}


inline
fixed CMathFixed::Clamp( fixed val, fixed min, fixed max )
{
	if( val < min )
		return min;
	else
	if( val > max )
		return max;
	
	return val;
}


inline
fixed CMathFixed::ClampToUnit( fixed val )
{
	// ASSERT( Abs( val ) < ClampToUnitError ); // removed by aNdy; for correct CMathFixed::ASin()
	
	if( val < -One )
		val = -One;
	else if( val > One )
		val = One;

	return val;
}


inline
fixed CMathFixed::DegreesToRadians( fixed degrees )
{
	return Mul( degrees, CMathFixed_PI ) / 180;
}


inline
fixed CMathFixed::RadiansToDegrees( fixed radians )
{
	return Div( radians * 180, CMathFixed_PI );
}


inline
fixed CMathFixed::Cos( fixed degrees )
{
	return Sin( degrees + CMathFixed::Degrees_90 );
}


inline
fixed CMathFixed::Tan( fixed degrees )
{
	fixed cos = Cos( degrees );
	
	return ( cos )? Div( Sin( degrees ), cos ) : MAX_INT32;
}

inline
fixed CMathFixed::Inv( fixed val )
{
	return Div( One, val );
}


inline
fixed CMathFixed::Lerp( fixed begin, fixed end, fixed val )
{
	return begin + CMathFixed::Mul( ( end - begin ), val );
}


inline
fixed CMathFixed::EaseInOut( fixed interpolation )
{
    return (CMathFixed::One - CMathFixed::Cos(interpolation*180))/2;
}


inline
fixed CMathFixed::EaseIn( fixed interpolation )
{
    return CMathFixed::One - CMathFixed::Cos(interpolation*90);
}


inline
fixed CMathFixed::EaseOut( fixed interpolation )
{
    return - CMathFixed::Cos(CMathFixed::Degrees_90 + interpolation*90);
}


#endif // #define __CMATHFIXED_H__
